/*
 * widgets.c
 *
 *  Created on: Jan 12, 2021
 *      Author: David    Original work by Jose Barros (PTDreamer), 2017
 */

#include "widgets.h"
#include "screen.h"
#include "oled.h"
#include "gui.h"
static char displayString[32];
static bool callFromCombo;

#if (COMBO_STYLE==COMBO_STYLE_SCROLL)
struct{
  uint8_t status;
  uint8_t after_action;
  int16_t offset;
  int16_t len;
  int16_t opt_len;
  uint32_t time;
  comboBox_item_t * last_item;
  screen_t * lastscr;
}scroll;
#endif

#ifdef __BASE_FILE__
#undef __BASE_FILE__
#endif
#define __BASE_FILE__ "widgets.c"

void newWidget(widget_t **new, widgetType type, struct screen_t *scr, void **content){
  widget_t *w=_malloc(sizeof(widget_t));
  if(!w || !scr) Error_Handler();
  switch(type){
    case widget_combo:
      w->content = _malloc(sizeof(comboBox_widget_t));
      break;
    case widget_display:
      w->content = _malloc(sizeof(displayOnly_widget_t));
      break;
    case widget_multi_option:
    case widget_editable:
      w->content = _malloc(sizeof(editable_widget_t));
      break;
    case widget_button:
    case widget_bmp_button:
      w->content = _malloc(sizeof(button_widget_t));
      break;
    default:
      Error_Handler();
  }
  if(!w->content) Error_Handler();
  screen_addWidget(w,scr);
  widgetDefaultsInit(w, type);
  if(new){
    *new = w;
  }
  if(content){
    *content = w->content;
  }

}

editable_widget_t *newEditable(widgetType type){
  editable_widget_t *edit=_malloc(sizeof(editable_widget_t));
  if(!edit) Error_Handler();
  editableDefaultsInit(edit,type);
  return edit;
}

comboBox_item_t *newComboItem(void){
  comboBox_item_t *item=_malloc(sizeof(comboBox_item_t));
  if(!item) Error_Handler();
  return item;
}

displayOnly_widget_t * extractDisplayPartFromWidget(widget_t *w) {
  if(!w)
    return NULL;
  switch (w->type) {
    case widget_display:
      return (displayOnly_widget_t*)w->content;
    case widget_editable:
    case widget_multi_option:
      return &((editable_widget_t*)w->content)->inputData;
    case widget_combo:
    {
      comboBox_widget_t *combo = (comboBox_widget_t*)w->content;
      if((combo->currentItem) && ((combo->currentItem->type==combo_Editable)||(combo->currentItem->type==combo_MultiOption))){
        return &combo->currentItem->widget->inputData;
      }
    }
    default:
      return NULL;
      break;
  }
  return NULL;
}

editable_widget_t * extractEditablePartFromWidget(widget_t *w) {
  if(!w)
    return NULL;
  switch (w->type) {
    case widget_editable:
    case widget_multi_option:
      return (editable_widget_t*)w->content;
    case widget_combo:
    {
      comboBox_widget_t *combo = (comboBox_widget_t*)w->content;
      if((combo->currentItem) && ((combo->currentItem->type==combo_Editable)||(combo->currentItem->type==combo_MultiOption))){
        return combo->currentItem->widget;
      }
    }
    default:
      return NULL;
      break;
  }
}

selectable_widget_t * extractSelectablePartFromWidget(widget_t *w) {
  if(!w)
    return NULL;
  switch (w->type) {
    case widget_editable:
    case widget_multi_option:
      return &((editable_widget_t*)w->content)->selectable;
    case widget_button:
    case widget_bmp_button:
      return &((button_widget_t*)w->content)->selectable;
    case widget_combo:
    {
      comboBox_widget_t *combo = (comboBox_widget_t*)w->content;
      if(callFromCombo){
        if((combo->currentItem) && ((combo->currentItem->type==combo_Editable)||(combo->currentItem->type==combo_MultiOption))){
          return &combo->currentItem->widget->selectable;
        }
      }
      else{
        return &combo->selectable;
      }
    }
    default:
      return NULL;
  }
}
void widgetDefaultsInit(widget_t *w, widgetType type){
  if(!w || !w->content){ return; }

  w->type = type;
  w->draw = &default_widgetDraw;
  w->update = &default_widgetUpdate;
  w->enabled = 1;
  w->frameType = frame_auto;
  w->refresh = refresh_idle;
  w->radius = DEFAULT_FRAME_R;
  w->posX = 0;
  w->posY = 0;
  w->width = 0;
  w->next_widget = NULL;
  comboBox_widget_t *combo;
  if(type==widget_combo){
    combo = (comboBox_widget_t*)w->content;
    combo->first = NULL;
    combo->currentItem = NULL;                    // Needs to be cleared before the extract from widget functions.
  }
  selectable_widget_t *sel = extractSelectablePartFromWidget(w);
  displayOnly_widget_t *dis = extractDisplayPartFromWidget(w);
  editable_widget_t *edit = extractEditablePartFromWidget(w);
  if(sel) {
    sel->previous_state = widget_idle;
    sel->state = widget_idle;
    sel->tab = 0;
    sel->processInput = &default_widgetProcessInput;
    sel->longPressAction = NULL;
  }
  if(dis){
    dis->font = DEFAULT_FONT;
    dis->getData = NULL;
    dis->reservedChars = 0;
    dis->number_of_dec = 0;
    dis->type = field_int32;
    dis->displayString=displayString;
    dis->endString=NULL;
    dis->stringStart=0;
    dis->last_value = 0;
    dis->textAlign = align_center;
    dis->dispAlign = align_disabled;
  }
  if(edit){
    edit->step=1;
    edit->big_step=1;
    edit->current_edit=0;
    edit->max_value=2147483647;
    edit->min_value=0;
    edit->setData=NULL;
  }
  switch (type) {
    case widget_multi_option:
      edit->max_value = 255;
      edit->numberOfOptions = 0;
      edit->options = NULL;
      break;
    case widget_combo:
      w->frameType = frame_combo;
      w->draw = &comboBoxDraw;
      w->parent->current_widget = w;
      combo->selectable.processInput = &comboBoxProcessInput;
      combo->font = DEFAULT_FONT;
      combo->currentScroll = 0;
      break;
    case widget_button:
    {
      button_widget_t* button = (button_widget_t*)w->content;
      button->dispAlign = align_disabled;
      button->textAlign = align_center;
      button->displayString = NULL;
      button->font = DEFAULT_FONT;
      button->action = NULL;
      break;
    }
    case widget_bmp_button:
    {
      button_widget_t* button = (button_widget_t*)w->content;
      button->dispAlign = align_disabled;
      button->action = NULL;
      button->xbm = NULL;
      button->last_xbm = NULL;
      break;
    }
    default:
      break;
  }

}
void editableDefaultsInit(editable_widget_t* editable, widgetType type){
  widget_t w;
  w.content=editable;
  widgetDefaultsInit(&w, type);
}

void insertDot(char *str, uint8_t dec){
  for(int x = strlen(str); x > (int)strlen(str) - (int)dec - 2; --x) {
    str[x + 1] = str[x];
  }
  str[strlen(str) - dec - 1] = '.';
}

void widgetEnable(widget_t* w){
  if(w && !w->enabled){
    w->enabled = 1;
    w->refresh = refresh_triggered;
  }
}

void widgetDisable(widget_t* w){
  if(w && w->enabled){
    w->enabled = 0;
    w->parent->state=screen_Erase;
  }
}

void default_widgetUpdate(widget_t *w) {
  if(!w){ return ; }
  if(!w->enabled){
    w->refresh=refresh_idle;
    return;
  }
  button_widget_t* button;
  bmp_widget_t* bmp;
  int32_t val_ui=0;
  displayOnly_widget_t* dis = extractDisplayPartFromWidget(w);
  editable_widget_t *edit = extractEditablePartFromWidget(w);

  switch(w->type){
    case widget_bmp_button:
      button = ((button_widget_t*)w->content);
      if(!button->xbm){
        widgetDisable(w);
        return;
      }
      break;
    case widget_button:
      button = ((button_widget_t*)w->content);
      if(!button->displayString){
        widgetDisable(w);
        return;
      }
      val_ui=strsum(button->displayString);
      break;
    case widget_bmp:
      bmp = ((bmp_widget_t*)w->content);
      if(!bmp->xbm){
        widgetDisable(w);
        return;
      }
      break;

    case widget_multi_option:
      if(!dis){
        widgetDisable(w);
        return;
      };
      val_ui=strsum(edit->options[*(uint8_t*)dis->getData()]);  // Get string sum
      break;

    case widget_display:
    case widget_editable:
      if(!dis){
        widgetDisable(w);
        return;
      }
      if(dis->type == field_string){
        val_ui=strsum(dis->getData());                          // Get string sum
        break;
      }
      else{
        val_ui = *(int32_t*)dis->getData();
        uint8_t sum = strsum(dis->endString);
        widgetDetectChange(w,(val_ui + sum));                   // Check for changes in value and endString
        return;
      }
      break;
    default:
      return;
  }
  widgetDetectChange(w,val_ui);
}

void widgetDetectChange(widget_t* w, int32_t val){
  displayOnly_widget_t* dis;
  bmp_widget_t* bmp;
  button_widget_t* button;
  bool refresh=0;
  switch(w->type){
    case widget_display:
    case widget_editable:
    case widget_multi_option:
      dis=extractDisplayPartFromWidget(w);
      if(dis->last_value!=val){
        dis->last_value=val;
        refresh=1;
      }
      break;

    case widget_button:
      button = (button_widget_t*)w->content;
      if(button->last_value != val){
        button->last_value = val;
        refresh=1;
      }
      break;

    case widget_bmp_button:
      button = (button_widget_t*)w->content;
      if(button->last_xbm != button->xbm){
        button->last_xbm = button->xbm;
        refresh=1;
      }
      break;

    case widget_bmp:
      bmp = (bmp_widget_t*)w->content;
      if(bmp->last_xbm != bmp->xbm){
        bmp->last_xbm = bmp->xbm;
        refresh=1;
      }
      break;

    default:
      break;
  }
  if(refresh && w->refresh==refresh_idle){
    w->refresh=refresh_triggered;
  }
}

// Clear widget field before drawing widget with new data
void widgetClearField(widget_t* w){
  if(!w){ return; }
  selectable_widget_t* sel = extractSelectablePartFromWidget(w);
  displayOnly_widget_t* dis = extractDisplayPartFromWidget(w);
  button_widget_t* button;
  bmp_widget_t* bmp;
  uint8_t cHeight = u8g2_GetMaxCharHeight(&u8g2);
  uint8_t r;

  if(w->frameType!=frame_combo){
    u8g2_SetDrawColor(&u8g2, BLACK);
    if(w->parent->state < screen_Erase ){
      switch((uint8_t)w->type){
        case widget_bmp:
          bmp = (bmp_widget_t*)w->content;
          u8g2_DrawBox(&u8g2, w->posX, w->posY, bmp->xbm->width, bmp->xbm->height);
          break;

        case widget_bmp_button:
          button = (button_widget_t*)w->content;
          u8g2_DrawBox(&u8g2, w->posX, w->posY, button->xbm->width+4, button->xbm->height+4);
          break;

        case widget_button:
          button = (button_widget_t*)w->content;
          u8g2_DrawBox(&u8g2, w->posX, w->posY, w->width, cHeight+1);
          break;

        case widget_display:
          u8g2_DrawBox(&u8g2, w->posX, w->posY, w->width, cHeight+1);       // Draw black square to erase previous data
          break;

        case widget_editable:
        case widget_multi_option:
          u8g2_DrawBox(&u8g2, w->posX ,w->posY, w->width, cHeight+1);
          break;
      }
    }
    if( (sel && (sel->state==widget_edit) && (dis->type!=field_string) && (w->frameType==frame_auto)) || (w->frameType==frame_solid) ){
      u8g2_SetDrawColor(&u8g2, WHITE);
      if(w->radius<0){
        r=(cHeight-1)/2;
      }
      else{
        r = w->radius;
      }
      if(sel){
        u8g2_DrawRBox(&u8g2, w->posX ,w->posY, w->width, cHeight+1,r );
      }
      else{
        u8g2_DrawRBox(&u8g2, w->posX ,w->posY, w->width+2, cHeight+1, r);
      }

      u8g2_SetDrawColor(&u8g2, XOR);
      return;
    }
    u8g2_SetDrawColor(&u8g2, WHITE);
  }
  else{                                             // In combo
    if(sel->state==widget_edit){                    // Being edited
      u8g2_SetDrawColor(&u8g2, XOR);                // Set XOR color
    }
    else{                                           // Not editing
      u8g2_SetDrawColor(&u8g2, WHITE);              // Set white color
    }
  }
}


void widgetAlign(widget_t* w){
  if(!w || w->frameType==frame_combo){ return; }

  uint8_t strWidth=0, stringStart=0, textAlign=align_disabled, dispAlign=align_disabled;
  displayOnly_widget_t *dis=extractDisplayPartFromWidget(w);
  selectable_widget_t *sel=extractSelectablePartFromWidget(w);
  editable_widget_t *edit = extractEditablePartFromWidget(w);
  button_widget_t* button = NULL;
  switch(w->type){
    case widget_button:
      button = (button_widget_t *)w->content;
      strWidth=u8g2_GetUTF8Width(&u8g2, button->displayString);
      break;

    case widget_display:
    case widget_editable:
      if(dis->type == field_string){
        strWidth=u8g2_GetUTF8Width(&u8g2, (char *)dis->getData());
      }
      else{
        strWidth=u8g2_GetUTF8Width(&u8g2, displayString);
      }
      break;

    case widget_multi_option:
      strWidth=u8g2_GetUTF8Width(&u8g2,  (char *)edit->options[*(uint8_t*)dis->getData()]);
      break;
    default:
      return;
  }
  if(!strWidth){                // Empty widget, wrong settings
    widgetDisable(w);           // Disable widget
    return;
  }
  if(dis){
    textAlign = dis->textAlign;
    dispAlign = dis->dispAlign;
  }
  else if(w->type == widget_button){
    textAlign = button->textAlign;
    dispAlign = button->dispAlign;
  }
  else{
    return;
  }
  if(sel && (w->frameType!=frame_disabled)){      // If selectable, extra space for not overlapping the frame
    if(w->width < strWidth+7){                    // If width too small
      if(strWidth+7<displayWidth){                   // If fits oled size
        w->width=strWidth+7;                      // Use width from str width
      }
      else{
        w->width = strWidth;                      // Else, don't add extra space
      }
    }
  }
  else{
    if(w->width < strWidth){                      // If width too small
      w->width=strWidth;                          // Use width from str width
    }
  }

  if(w->width > displayWidth){
    if(textAlign != align_disabled)
      textAlign = align_left;
    if(dispAlign != align_disabled)
      dispAlign = align_left;
  }

  switch(dispAlign){
    case align_disabled:
      break;
    case align_center:
      w->posX =(displayWidth - w->width)/2;
      break;
    case align_right:
      w->posX = displayWidth - w->width;
      break;
    case align_left:
    default:
      w->posX = 0;
      break;
  }

  switch(textAlign){
    case align_disabled:
      break;
    case align_center:
      stringStart = (w->posX + ((w->width-strWidth)/2));
      break;
    case align_right:
      stringStart =(w->posX + (w->width-strWidth));
      if(sel){
        if(stringStart >= 3){
          stringStart -= 3;
        }
      }
      break;
    case align_left:
    default:
      if(sel && ((w->posX+3)<=displayWidth)){
        stringStart=w->posX+3;
      }
      else{
        stringStart = w->posX;
      }
      break;
  }

  if(w->type == widget_button){
    button->stringStart=stringStart;
  }
  else if(dis){
    dis->stringStart = stringStart;
  }
}

uint8_t default_widgetDraw(widget_t *w) {
  if(!w || !w->enabled){ return 0; }

  bool frameDraw = 0;
  bool frameColor = BLACK;
  displayOnly_widget_t* dis = extractDisplayPartFromWidget(w);
  editable_widget_t* edit = extractEditablePartFromWidget(w);
  selectable_widget_t* sel = extractSelectablePartFromWidget(w);
  button_widget_t* button = NULL;
  bmp_widget_t* bmp = NULL;
  uint8_t refresh = w->refresh | w->parent->state;
  uint8_t cHeight = 0;

  if( dis ){
    if(u8g2.font != dis->font){
      u8g2_SetFont(&u8g2, dis->font);
    }
    cHeight = u8g2_GetMaxCharHeight(&u8g2);
  }
  else if((w->type == widget_button)||(w->type == widget_bmp_button)){
    button = (button_widget_t*)w->content;
    if(w->type == widget_button){
      if(u8g2.font != button->font){
        u8g2_SetFont(&u8g2, button->font);
      }
      cHeight = u8g2_GetMaxCharHeight(&u8g2);
    }
  }
  else if(w->type == widget_bmp){
    bmp = (bmp_widget_t*)w->content;
  }
  if(w->frameType==frame_outline){
    frameDraw=1;
    frameColor = WHITE;
  }
  else if(w->frameType==frame_auto){
    if(sel) {
      if(refresh==refresh_idle){                      // If not forced refresh, check for changes
        switch (sel->state) {
          case widget_selected:
            if(sel->previous_state != widget_selected){
              frameDraw=1;
              frameColor = WHITE;
              if(sel->previous_state == widget_edit){
                refresh=refresh_triggered;
              }
            }
            break;
          case widget_idle:
            if(sel->previous_state != widget_idle){
              frameDraw=1;
            }
            break;
          case widget_edit:
            if(sel->previous_state != widget_edit){
              refresh=refresh_triggered;
            }
            break;
          default:
            return 0;
        }
      }
      else{                                           // Else, redraw frames
        switch (sel->state) {
          case widget_selected:
              frameDraw=1;
              frameColor = WHITE;
            break;
          case widget_idle:
          case widget_edit:
            break;
          default:
            return 0;
        }
      }
    }
  }

  if(refresh){
    if(dis && dis->type==field_int32){
      int32_t val_ui = *(int32_t*)dis->getData();
      uint8_t decimals = dis->number_of_dec+1;
      if(val_ui<0){
        decimals++;
      }
      if(decimals>10){ decimals=10; }
      snprintf(dis->displayString, dis->reservedChars+1, "%0*ld", decimals, (int32_t)val_ui);    // Convert value into string

      uint8_t dispLen=strlen(dis->displayString);
      uint8_t endLen=strlen(dis->endString);
      if(dis->number_of_dec){                                                 // If there're decimals
        if(dis->reservedChars >= (dispLen+1)){                                // Ensure there's enough space in the string for adding the decimal point
          insertDot(dis->displayString,  dis->number_of_dec);                 // Insert decimal dot
        }
      }
      if(dis->reservedChars >= (dispLen+endLen)){                             // Ensure there's enough space in the string for adding the end String
        strcat(dis->displayString, dis->endString);                           // Append endString
      }
      dis->displayString[dis->reservedChars]=0;                               // Ensure last string char is 0
    }

    widgetAlign(w);        // Align
    widgetClearField(w);        // Clear old field
    if(w->refresh==refresh_triggered){
      w->refresh=refresh_idle;
    }

    switch(w->type){

      case widget_bmp:
        u8g2_SetDrawColor(&u8g2, WHITE);
        u8g2_DrawXBMP(&u8g2, w->posX, w->posY, bmp->xbm->width, bmp->xbm->height, bmp->xbm->xbm);
        return 1;

      case widget_bmp_button:
        u8g2_SetDrawColor(&u8g2, WHITE);
        u8g2_DrawXBMP(&u8g2, w->posX+2, w->posY+2, button->xbm->width, button->xbm->height, button->xbm->xbm);
        break;

      case widget_button:
        u8g2_DrawUTF8(&u8g2, button->stringStart, w->posY+2,  button->displayString);
        break;

      case widget_display:
        if(dis->type == field_string){
          u8g2_DrawUTF8(&u8g2, dis->stringStart, w->posY,  dis->getData());
        }
        else{
          u8g2_DrawUTF8(&u8g2, dis->stringStart, w->posY,  dis->displayString);
        }
        break;

      case widget_editable:
        if(dis->type == field_string){
          u8g2_DrawUTF8(&u8g2,  dis->stringStart, w->posY+2,  dis->getData());
          if(sel->state == widget_edit){
            char t[20];
            strcpy(t,dis->getData());

            t[edit->current_edit+1]=0;
            uint8_t x2=u8g2_GetUTF8Width(&u8g2, t);

            t[edit->current_edit]=0;
            uint8_t x1=u8g2_GetUTF8Width(&u8g2, t);

            u8g2_SetDrawColor(&u8g2, BLACK);
            u8g2_DrawBox(&u8g2, dis->stringStart, w->posY+ cHeight, w->width, 2);

            u8g2_SetDrawColor(&u8g2, WHITE);
            u8g2_DrawBox(&u8g2,  dis->stringStart+x1, w->posY+ cHeight, x2-x1, 2);

          }
          else if(sel->previous_state == widget_edit){
            u8g2_SetDrawColor(&u8g2, BLACK);
            u8g2_DrawBox(&u8g2, dis->stringStart, w->posY+ cHeight, w->width, 2);
          }
        }
        else{
          u8g2_DrawUTF8(&u8g2,dis->stringStart, w->posY+2,  dis->displayString);
        }
        break;

      case widget_multi_option:
        u8g2_DrawUTF8(&u8g2, dis->stringStart, w->posY+2, edit->options[*(uint8_t*)dis->getData()]);// Draw string
        break;

      default:
        return 0;
    }
  }

  if(sel) {
    sel->previous_state = sel->state;
  }

  if(frameDraw ||(refresh && w->frameType==frame_outline)){
    switch(w->type){
      case widget_bmp_button:
        u8g2_SetDrawColor(&u8g2, frameColor);
        u8g2_DrawRFrame(&u8g2, w->posX, w->posY, button->xbm->width+4,  button->xbm->height+4, 4);
        break;
      case widget_editable:
      case widget_button:
      case widget_multi_option:
        u8g2_SetDrawColor(&u8g2, frameColor);
        uint8_t r;
        if(w->radius<0){
          r=(cHeight-1)/2;
        }
        else{
          r = w->radius;
        }
        u8g2_DrawRFrame(&u8g2, w->posX ,w->posY, w->width, cHeight+1, r);
      default:
        break;
    }
    refresh=1;                                    // For drawing detection
  }
  u8g2_SetDrawColor(&u8g2, WHITE);
  return refresh;
}

uint16_t comboDrawProcessEditable(widget_t *w, comboBox_item_t *item){
  editable_widget_t* edit = item->widget;
  displayOnly_widget_t* dis = &edit->inputData;
  uint16_t opt_len = 0;

  if(item->type==combo_MultiOption){
    opt_len = u8g2_GetUTF8Width(&u8g2,edit->options[*(uint8_t*)dis->getData()]);
  }
  else if(item->type==combo_Editable){
    if(dis->type==field_int32){
      int32_t val_ui = *(int32_t*)dis->getData();                         // Get data
      uint8_t decimals = dis->number_of_dec+1;                            // Load decimal count
      if(val_ui<0){                                                       // If negative, add a decimal (decimals are just used as min char output in sprintf)
        decimals++;
      }
      if(decimals>10){ decimals=10; }                                     // Limit max decimals
      snprintf(dis->displayString, dis->reservedChars+1, "%0*ld", decimals, (int32_t)val_ui);    // Convert value into string
      uint8_t dispLen=strlen(dis->displayString);                         // Get string len
      uint8_t endLen=strlen(dis->endString);                              // Get endStr len
      if(dis->number_of_dec){                                             // If there're decimals
        if(dis->reservedChars >= (dispLen+1)){                            // Ensure there's enough space in the string for adding the decimal point
          insertDot(dis->displayString,  dis->number_of_dec);             // Insert decimal dot
        }
      }
      if(dis->reservedChars >= (dispLen+endLen)){                         // Ensure there's enough space in the string for adding the end String
        strcat(dis->displayString, dis->endString);                       // Append endString
      }
      dis->displayString[dis->reservedChars]=0;                           // Ensure last string char is 0
      opt_len=u8g2_GetUTF8Width(&u8g2,dis->displayString);
    }
    else if(dis->type==field_string || dis->type==field_hex){
      strncpy(displayString,dis->getData(),dis->reservedChars+1);
      opt_len=u8g2_GetUTF8Width(&u8g2,displayString);
    }
  }
  return(opt_len);
}

uint8_t comboBoxDraw(widget_t *w) {
  if(!w || !w->enabled){ return 0; }                                                    // Return if null or disabled

  comboBox_widget_t* combo = (comboBox_widget_t*)w->content;
  if(!combo){ return 0; }                                                               // Return if null
  comboBox_item_t *item = combo->first;
  if(!item){ return 0; }                                                                // Return if null

  uint16_t yDim = displayHeight - w->posY;
  uint8_t height;
  int8_t frameY=0;
  int8_t posY;
  bool drawFrame=1;
  uint8_t current_scroll = 0;
  uint8_t r;
  int16_t offset=0;                                                                     // Only modified in scroll mode, otherwise always 0
  uint16_t len = 0, opt_len = 0;

  if(w->refresh==refresh_idle && w->parent->state==screen_Idle){                        // If screen and widget idling
#if (COMBO_STYLE == COMBO_STYLE_SCROLL)
      if(scroll.status == scroll_restart){
        scroll.offset = 0;
        scroll.status = scroll_running;
      }
      if((scroll.status == scroll_running) && (current_time-scroll.time)>(SLIDE_TIME)){              // If text sliding running, update every 20mS
        scroll.time=current_time;
        scroll.status = scroll_refresh;
        if((scroll.len - ++scroll.offset) < (-SLIDE_SPACE + 5)){                          // Increase x offset, check limits
          scroll.offset = 0;
          if(scroll.len < displayWidth - scroll.opt_len - 12){                            // If the option field became smaller, stop after the scroll is over
            scroll.after_action = scroll_finish;
          }
        }
      }
      else {                                                                            // If nothing to do, return
        return 0;
      }
  }
  else{
    uint16_t limit;
    if(u8g2.font != combo->font){
      u8g2_SetFont(&u8g2, combo->font);
    }
    if((scroll.lastscr != w->parent) || (scroll.last_item != combo->currentItem)){                                          // If screen/widget refresh triggered, check if item or screen is different and reset offset
      scroll.offset=0;
      scroll.lastscr = w->parent;
      scroll.last_item = combo->currentItem;
    }
    else if(scroll.status >= scroll_running){
      scroll.after_action = scroll_keep;                                                 // Otherwise, keep offset after redrawing
    }
    scroll.status = scroll_restart;
    scroll.len = u8g2_GetUTF8Width(&u8g2, combo->currentItem->text);                     // Compute string length and limit only once
    if(combo->currentItem->type==combo_Editable || combo->currentItem->type==combo_MultiOption){                      // For editable widgets, limit is ~half of the oled width
      scroll.opt_len = comboDrawProcessEditable(w, combo->currentItem);                        // Process widget and get option length
      limit = displayWidth - scroll.opt_len - 12;
    }
    else{
      limit = displayWidth-8;                                                     // Else, use all the space available for the label
    }
    if(scroll.len < limit){                                                          // Disable if label text shorter than limit (No need to scroll text)
      scroll.status = scroll_disabled;
    }
#else
    return 0;
#endif
  }
                                                                                      // If we got here, something needs refreshing
  if(w->refresh!=refresh_idle){                                                       // Widget not idle?
    if(w->refresh==refresh_triggered)                                                 // Only clear if state=refresh_triggered
        w->refresh=refresh_idle;                                                      // Otherwise, keep refresh_always
    if(w->parent->state != screen_Erased){                                            // If it was the widget and the screen was not erased already
      w->parent->state = screen_Erased;                                               //
      fillBuffer(BLACK, fill_dma);                                                    // Erase fast using dma
    }
  }
  if(u8g2.font != combo->font){
    u8g2_SetFont(&u8g2, combo->font);
  }
  height= u8g2_GetMaxCharHeight(&u8g2)+1;                                             // +1 to allow separation between combobox items

  if(w->radius<0){
    r=(height-1)/2;
  }
  else{
    r = w->radius;
  }

  while(current_scroll < combo->currentScroll) {
    if(!item->next_item)
      break;
    item = item->next_item;
    if(item->enabled)
      ++current_scroll;
  }
  for(uint8_t y = 0; y < yDim / height; ++y) {

    uint8_t align = item->dispAlign;
    bool editable = (item->type==combo_Editable)||(item->type==combo_MultiOption);

    if(item == combo->currentItem) {                                                  // If this is the current combo item
      frameY=y * height + w->posY;                                                    // Store frame position, drawn at the end
#if (COMBO_STYLE == COMBO_STYLE_SCROLL)
      offset=scroll.offset;                                                            // Get text drawing offset
      len = scroll.len;                                                                // Get text len
      opt_len = scroll.opt_len;
      if(scroll.status == scroll_refresh){                                              // If only refreshing sliding text and this is the matching item
        scroll.status = scroll_refresh_this;
        u8g2_SetDrawColor(&u8g2, BLACK);
        if (editable){                                                                // Clear textfield
          u8g2_DrawBox(&u8g2, 2, frameY+2,  displayWidth-2-opt_len-12, height-2);
        }
        else{
          u8g2_DrawBox(&u8g2, 2, frameY+2, displayWidth-4, height-2);
        }
        u8g2_SetDrawColor(&u8g2, WHITE);
      }
#endif
    }
    else{
      offset=0;                                                                       // Use 0 offset for not selected item (Not scrolling)
      len = u8g2_GetUTF8Width(&u8g2, item->text);                                     // Get len
    }

#if (COMBO_STYLE == COMBO_STYLE_SCROLL)
    if((scroll.status == scroll_refresh_this) || (scroll.status < scroll_running)){                 // Only continue if no sliding text active or disabled (full redraw) or this is the item that needs updating
#else
      len = u8g2_GetUTF8Width(&u8g2, item->text);
#endif
      if (!editable){                                                   // If screen or action item, just draw the label
        if(len>displayWidth-9){                                         // If too long, override with left align
          align=align_left;
        }
        u8g2_SetClipWindow(&u8g2, 4, 0, displayWidth-4, displayHeight-1);
        if(align==align_left){
          u8g2_DrawUTF8(&u8g2, (int16_t)4-offset, y * height + w->posY +2, item->text);       // Draw string
#if (COMBO_STYLE == COMBO_STYLE_SCROLL)
          if(offset){
            int16_t end =len - offset;
            if(end<=(displayWidth - 4 - SLIDE_SPACE)){
              u8g2_DrawUTF8(&u8g2, end + SLIDE_SPACE, y * height + w->posY +2, item->text);       // Draw beginning of string
            }
          }
#endif
        }
        else{
          if(align==align_right){
            u8g2_DrawUTF8(&u8g2, displayWidth-3-len, y * height + w->posY +2, item->text);
          }
          else{     // Align center
            u8g2_DrawUTF8(&u8g2, (displayWidth-1-len)/2, y * height + w->posY +2, item->text);
          }
        }
      }
      else{                                                                                       // Editable or multioption
        editable_widget_t* edit = item->widget;
        displayOnly_widget_t* dis = &edit->inputData;
        selectable_widget_t *sel = &edit->selectable;

        posY = y * height + w->posY;                                                              // Set drawing Ypos same as the current combo option
        opt_len = comboDrawProcessEditable(w, item);
        u8g2_SetDrawColor(&u8g2, WHITE);
#if (COMBO_STYLE == COMBO_STYLE_SCROLL)
        if(scroll.status < scroll_running){                                                       // Draw the editable data if not coming for text scroll.
#endif
          if(sel->state==widget_edit){                                                              // If edit mode
            drawFrame=0;
#if (COMBO_STYLE == COMBO_STYLE_FULL)
            u8g2_DrawRBox(&u8g2, 0, frameY, displayWidth, height, r);                                           // Highlight the entire item
#else
            u8g2_DrawRBox(&u8g2, displayWidth-opt_len-8, frameY, opt_len+8, height, r);                                 // Highlight only the variable width, the rest is used for the label
#endif
            u8g2_SetDrawColor(&u8g2, BLACK);
          }

          dis->stringStart = displayWidth-opt_len-5;                                   // Align to the left measuring actual string width
          if(item->type==combo_Editable){
            if(((dis->type==field_string || dis->type==field_hex) && (sel->state==widget_edit))){
              char str[sizeof(displayString)+1];
              uint8_t start,width;
              strcpy(str,displayString);
              str[edit->current_edit+1]=0;
              width=u8g2_GetUTF8Width(&u8g2, str);
              str[edit->current_edit]=0;
              start=u8g2_GetUTF8Width(&u8g2, str);
              width-=start;
              u8g2_DrawRBox(&u8g2, dis->stringStart+start, posY+1, width+1, height-2,2);
              u8g2_SetDrawColor(&u8g2, XOR);
              u8g2_DrawUTF8(&u8g2,dis->stringStart, posY+2, displayString);
            }
            else{
              u8g2_DrawUTF8(&u8g2,dis->stringStart, posY+2, dis->displayString);
            }
          }
          else if(item->type==combo_MultiOption){
            u8g2_DrawUTF8(&u8g2,dis->stringStart, posY+2,  edit->options[*(uint8_t*)dis->getData()]);
          }
#if (COMBO_STYLE == COMBO_STYLE_SCROLL)
        }
#endif
#if (COMBO_STYLE != COMBO_STYLE_FULL)
        else if(sel->state==widget_edit){                                                              // If edit mode
          drawFrame=0;
        }
        u8g2_SetDrawColor(&u8g2, WHITE);
#endif
        u8g2_SetClipWindow(&u8g2, 4, 0,  displayWidth-2-opt_len-11, displayHeight-1);
        u8g2_DrawUTF8(&u8g2, (int16_t)4-offset, y * height + w->posY +2, item->text);       // Draw string
#if (COMBO_STYLE == COMBO_STYLE_SCROLL)                                           // Calculate end position
          if(offset){
            int16_t end = len - offset;
            if(end<=(displayWidth-2-opt_len-12 - SLIDE_SPACE)){
              u8g2_DrawUTF8(&u8g2, end + SLIDE_SPACE, y * height + w->posY +2, item->text);       // Draw beginning of string
            }
          }
#endif
      }
      u8g2_SetMaxClipWindow(&u8g2);
#if (COMBO_STYLE == COMBO_STYLE_SCROLL)
      if(scroll.status == scroll_refresh_this){                             // Done refreshing, set running state
        scroll.status = scroll_running;
      }
    }
#endif
    do {
      item = item->next_item;
    }while(item && !item->enabled);                                                                     // Find next enabled item

    if(!item){
      break;
    }
  }
  if(drawFrame){
    u8g2_DrawRFrame(&u8g2, 0, frameY, displayWidth, height,  r);
  }

#if (COMBO_STYLE == COMBO_STYLE_SCROLL)
  if(scroll.after_action == scroll_keep){
    scroll.after_action = 0;                                                                   // Restore status
    scroll.status = scroll_running;
  }
  if(scroll.after_action == scroll_finish){
    scroll.after_action = 0;                                                                   // Restore status
    scroll.status = scroll_disabled;
  }
#endif
  return 1;
}

int comboBoxProcessInput(widget_t *w, RE_Rotation_t input, RE_State_t *state) {

  if(!w || !w->enabled || input == Rotate_Nothing ){
    return -1;
  }
  comboBox_widget_t* combo = (comboBox_widget_t*)w->content;


  if(u8g2.font != combo->font){
    u8g2_SetFont(&u8g2, combo->font);
  }

  uint8_t firstIndex = combo->currentScroll;
  uint16_t yDim = displayHeight - w->posY;
  uint16_t height = u8g2_GetMaxCharHeight(&u8g2)+1;
  uint8_t maxIndex = yDim / height;
  uint8_t lastIndex = combo->currentScroll + maxIndex -1;
  selectable_widget_t *sel;
  if(w->refresh==refresh_idle){
    w->refresh=refresh_triggered;                                                                             // Update in combo erases whole screen (to avoid possible leftovers)
  }
  if((input == Click) || (input == LongClick)){                                                               // If clicked
    if (combo->currentItem->type==combo_Action){                                                              // If combo Action type
      return combo->currentItem->action(w, input);                                                                   // Process action
    }
    else if (combo->currentItem->type==combo_Screen){                                                         // If combo screen type
      return combo->currentItem->action_screen;                                                               // Return screen index
    }
  }
  if(input!=Rotate_Nothing){
    if ((combo->currentItem->type==combo_Editable)||(combo->currentItem->type==combo_MultiOption)){           // If combo option type
      sel = &combo->currentItem->widget->selectable;                                                            // Get selectable data
      combo->selectable.state = sel->state;
      combo->selectable.previous_state = sel->previous_state;
      if(((input == Click) && (sel->state!=widget_edit)) || sel->state==widget_edit){                         // If widget in edit mode
        callFromCombo=1;
        sel->processInput(w, input, state);                                                                   // Process widget
        callFromCombo=0;
        return -1;                                                                                            // Return
      }
    }
    if(input == Rotate_Increment) {
      //Set comboBox item to next comboBox
      comboBox_item_t *current = combo->currentItem->next_item;
      // if comboBox is disabled, skip. While comboBox still valid
      while(current && !current->enabled) {
        current = current->next_item;
      }
      // If comboBox valid(didn't reach end)
      if(current) {
        combo->currentItem = current;
        uint8_t index = comboItemToIndex(w, current);
        if(index > lastIndex)
          ++combo->currentScroll;
      }
    }
    else if(input == Rotate_Decrement) {
      comboBox_item_t *current = NULL;
      // If comboBox is not the first element
      if(combo->currentItem != combo->first){
        do {
          current = combo->first;
          while(current->next_item != combo->currentItem) {
            current = current->next_item;
          }
          combo->currentItem = current;
        }while(!current->enabled);
        uint8_t index = comboItemToIndex(w, current);
        if(index < firstIndex)
          --combo->currentScroll;
      }
    }
  }
  return -1;
}

int32_t strsum(char* str){
  int32_t sum=0;
  while(*str){
    sum+=*str;
    str++;
  }
  return sum;
}

//returns -1 if processed, -2 if not processed, or next screen
int default_widgetProcessInput(widget_t *w, RE_Rotation_t input, RE_State_t *state){
  if(!w || !w->enabled || (input == Rotate_Nothing)){ return -1;  }

  selectable_widget_t* sel = extractSelectablePartFromWidget(w);
  if(!sel) {  return -2;  }

  displayOnly_widget_t* dis = extractDisplayPartFromWidget(w);
  editable_widget_t* edit = extractEditablePartFromWidget(w);
  comboBox_widget_t* combo = NULL;

  if(w->type==widget_combo){
    combo = (comboBox_widget_t*)w->content;
  }
  if(input == LongClick) {
    if(sel->longPressAction){
      return sel->longPressAction(w);
    }
    else{
      input = Click;
    }
  }

  if(input == Click) {
    switch (sel->state) {
      case widget_selected:
        if(w->refresh==refresh_idle){
          w->refresh = refresh_triggered;
        }
        if((w->type == widget_button)||(w->type == widget_bmp_button)){
          return ((button_widget_t*)w->content)->action(w);
        }
        if(dis->type == field_string || dis->type == field_hex){
          strcpy(dis->displayString, (char*)edit->inputData.getData());
          edit->current_edit = 0;
        }
        sel->state = widget_edit;
        sel->previous_state = widget_selected;
        break;

      case widget_edit:
        if(w->refresh==refresh_idle){
          w->refresh = refresh_triggered;
        }
        if(dis->type == field_string || dis->type == field_hex) {
          ++edit->current_edit;
          if(edit->current_edit == dis->reservedChars){
            sel->state = widget_selected;
            sel->previous_state = widget_edit;
            edit->current_edit = 0;
          }
        }
        else {
          sel->state = widget_selected;
          sel->previous_state = widget_edit;
        }
        break;

      default:
        break;
    }
    return -1;
  }
  if( edit && sel->state==widget_edit) {
    int32_t val_ui;
    int32_t inc;
    if(w->refresh==refresh_idle){
      w->refresh=refresh_triggered;
    }
    if(abs(state->Diff) > 1){
      inc = edit->big_step;
    }
    else{
      inc = edit->step;
    }
    if(state->Diff < 0){
        inc = -inc;
    }
    if( (w->type == widget_multi_option || (combo && combo->currentItem->type==combo_MultiOption)) ) {
        int32_t option = *(int32_t*)dis->getData();
        if(input == Rotate_Increment){
          if(option < edit->numberOfOptions -1)
            option++;
          else
            option = edit->numberOfOptions -1;
        }
        else if(input == Rotate_Decrement){
          if(option > 0)
            option--;
          else
            option=0;
        }
        edit->setData(&option);
    }
    else if(dis->type == field_string || dis->type == field_hex){
      if(input == Rotate_Decrement_while_click ||input == Rotate_Increment_while_click){
        if(input == Rotate_Decrement_while_click){
          if(edit->current_edit==0){
            edit->current_edit=dis->reservedChars-1;
          }
          else{
            edit->current_edit--;
          }
        }
        else{
          if(edit->current_edit<dis->reservedChars-1){    // Number of editing chars is defined reserved chars.
            edit->current_edit++;
          }
          else{
            edit->current_edit=0;
          }
        }
        return -1;
      }
      char *s = (char*)dis->getData();
      char current_edit = s[edit->current_edit] + inc;
      if(dis->type == field_string){
        switch(current_edit){           // Chars: Space -./ 0-9 A-Z
          case ' '-1:
            current_edit ='Z';
            break;
          case '!':
            current_edit ='-';
            break;
          case ',':
            current_edit =' ';
            break;
          case ':':
            current_edit ='A';
            break;
          case '@':
            current_edit ='9';
            break;
          case 'Z'+1:
            current_edit =' ';
            break;
          default:
            break;
        }
      }
      else if(dis->type == field_hex){  // field_hex is just field_string with different ranges
        switch(current_edit){           // Range is 0-9, A-F
          case '0'-1:
            current_edit ='F';
            break;
          case '9'+1:
            current_edit = 'A';
            break;
          case 'A'-1:
            current_edit = '9';
            break;
          case 'F'+1:
            current_edit ='0';
            break;
          default:
            break;
        }
      }
      s[edit->current_edit]=(char)current_edit;
      edit->setData(s);
    }
    else if(dis->type==field_int32){
      if(!dis->displayString){                            // If empty
        widgetDisable(w);                                 // This shouldn't happen. Disable widget to avoid possible errors.
        return -1;
      }
      val_ui = *(int32_t*)dis->getData();

      if( (inc>0) && ((val_ui+inc) < val_ui) ){           // Check for overflow
        val_ui = edit->max_value;
      }
      else if( (inc<0) && ((val_ui+inc) > val_ui) ){      // Check for underflow
        val_ui = edit->min_value;
      }
      else{
        val_ui += inc;
        if(val_ui < edit->min_value) {                    // Check limits
          val_ui = edit->min_value;
        }
        if(val_ui > edit->max_value) {
          val_ui= edit->max_value;
        }
      }
      edit->setData(&val_ui);
    }
    return -1;
  }
  else if (sel->state == widget_selected) {
    uint8_t next = 0xFF;
    int previous = -1;
    widget_t *next_w = NULL;
    widget_t *previous_w = NULL;
    widget_t *first_w = w;
    widget_t *last_w = w;
    widget_t *scan = w->parent->widgets;
    while(scan) {
      selectable_widget_t *e = extractSelectablePartFromWidget(scan);
      if(e) {
        if((e->tab > sel->tab) && (e->tab < next) && scan->enabled) {
          next = e->tab;
          next_w =scan;
        }
        if((e->tab < sel->tab) && (e->tab > previous) && scan->enabled) {
          previous = e->tab;
          previous_w = scan;
        }
        if((e->tab < extractSelectablePartFromWidget(first_w)->tab) && scan->enabled)
          first_w = scan;
        if((e->tab > extractSelectablePartFromWidget(last_w)->tab) && scan->enabled)
          last_w = scan;
      }
      scan = scan->next_widget;
    }
    if(next_w == NULL)
      next_w = first_w;
    if(previous_w == NULL)
      previous_w = last_w;
    if((input == Rotate_Increment) && next_w && (next_w != w)) {
      sel->state = widget_idle;
      sel->previous_state = widget_selected;
      w->parent->current_widget = next_w;
      extractSelectablePartFromWidget(next_w)->previous_state = extractSelectablePartFromWidget(next_w)->state;
      extractSelectablePartFromWidget(next_w)->state = widget_selected;
      return -1;
    }
    else if((input == Rotate_Decrement) && previous_w && (previous_w != w)) {
      sel->state = widget_idle;
      sel->previous_state = widget_selected;
      w->parent->current_widget = previous_w;
      extractSelectablePartFromWidget(previous_w)->previous_state = extractSelectablePartFromWidget(previous_w)->state;
      extractSelectablePartFromWidget(previous_w)->state = widget_selected;
      return -1;
    }
  }
  return -2;
}

void newComboScreen(widget_t *w, char *label, uint8_t actionScreen, comboBox_item_t **newItem){
  comboBox_item_t *item = _malloc(sizeof(comboBox_item_t));
  if(!item || !w || !label){
    Error_Handler();
  }
  comboBox_widget_t* combo = (comboBox_widget_t*)w->content;
  item->text = label;
  item->next_item = NULL;
  item->action_screen = actionScreen;
  item->type = combo_Screen;
  item->enabled = 1;
  item->dispAlign= align_center;

  comboBox_item_t *next = combo->first;

  if(!next) {
    combo->first = item;
    combo->currentItem = item;
  }
  else{
    while(next->next_item){
      next = next->next_item;
    }
    next->next_item = item;
  }
  if(newItem){
    *newItem = item;
  }
}

// Special case for editable strings, they need their own display buffer
void newComboEditableString(widget_t *w, char *label, editable_widget_t **newEdit, comboBox_item_t **newItem, char *dispBf){
  editable_widget_t *edit;
  newComboEditable(w, label, &edit, newItem);
  edit->inputData.displayString=dispBf;
  if(newEdit){
    *newEdit = edit;
  }
}

void newComboEditable(widget_t *w, char *label, editable_widget_t **newEdit, comboBox_item_t **newItem){
  comboBox_item_t *item = _malloc(sizeof(comboBox_item_t));
  editable_widget_t *edit = _malloc(sizeof(editable_widget_t));
  if(!item || !w || !label || !edit ){
    Error_Handler();
  }
  editableDefaultsInit(edit, widget_editable);
  comboBox_widget_t* combo = (comboBox_widget_t*)w->content;
  item->text = label;
  item->next_item = NULL;
  item->widget = edit;
  item->type = combo_Editable;
  item->enabled = 1;
  item->dispAlign = 0; // Unused, automatically adjusted

  comboBox_item_t *next = combo->first;
  edit->selectable.state=widget_selected;
  edit->selectable.previous_state=widget_selected;

  if(!next) {
    combo->first = item;
    combo->currentItem = item;
  }
  else{
    while(next->next_item){
      next = next->next_item;
    }
    next->next_item = item;
  }
  if(newItem){
    *newItem = item;
  }
  if(newEdit){
    *newEdit = edit;
  }
}

void newComboMultiOption(widget_t *w, char *label, editable_widget_t **newEdit, comboBox_item_t **newItem){
  comboBox_item_t *item = _malloc(sizeof(comboBox_item_t));
  editable_widget_t *edit = _malloc(sizeof(editable_widget_t));
  if(!item || !w || !label || !edit ){
    Error_Handler();
  }
  editableDefaultsInit(edit, widget_multi_option);
  comboBox_widget_t* combo = (comboBox_widget_t*)w->content;

  item->text = label;
  item->next_item = NULL;
  item->widget = edit;
  item->type = combo_MultiOption;
  item->enabled = 1;
  item->dispAlign = 0; // Unused, automatically adjusted
  edit->selectable.state=widget_selected;
  edit->selectable.previous_state=widget_selected;
  comboBox_item_t *next = combo->first;

  if(!next) {
    combo->first = item;
    combo->currentItem = item;
  }
  else{
    while(next->next_item){
      next = next->next_item;
    }
    next->next_item = item;
  }
  if(newItem){
    *newItem = item;
  }
  if(newEdit){
    *newEdit = edit;
  }
}

void newComboAction(widget_t *w, char *label, int (*action)(widget_t *w, RE_Rotation_t input), comboBox_item_t **newItem){
  comboBox_item_t *item = _malloc(sizeof(comboBox_item_t));
  if(!item || !w || !label || !action){
    Error_Handler();
  }
  comboBox_widget_t* combo = (comboBox_widget_t*)w->content;
  item->text = label;
  item->next_item = NULL;
  item->action = action;
  item->type = combo_Action;
  item->enabled = 1;
  item->dispAlign= align_center;
  comboBox_item_t *next = combo->first;

  if(!next) {
    combo->first = item;
    combo->currentItem = item;
  }
  else{
    while(next->next_item){
      next = next->next_item;
    }
    next->next_item = item;
  }
  if(newItem){
    *newItem = item;
  }
}

void comboResetIndex(widget_t *w){
  comboBox_widget_t* combo = (comboBox_widget_t*)w->content;
  if((combo->currentItem->type==combo_Editable)||(combo->currentItem->type==combo_MultiOption)){
      selectable_widget_t* sel = &combo->currentItem->widget->selectable;
    if(sel){
      sel->state = widget_selected;
      sel->previous_state = widget_selected;
    }
  }
  combo->currentItem = combo->first;
  combo->currentScroll=0;
}

uint8_t comboItemToIndex(widget_t *w, comboBox_item_t *item) {
  if(!w || !item){ return 0; }
  uint8_t index = 0;
  comboBox_item_t *i = ((comboBox_widget_t*)w->content)->first;
  while(i->next_item && i != item) {
    i = i->next_item;
    if(i->enabled)
      index++;
  }
  return index;
}

comboBox_item_t *comboIndexToItem(widget_t *w, uint8_t index) {
  if(!w){ return NULL; }
  comboBox_widget_t *combo = (comboBox_widget_t*)w->content;
  comboBox_item_t *i=combo->first;
  if(!i){ return NULL; }
  while(i->next_item && index) {
      i = i->next_item;
      if(i->enabled){
        index--;
      }
   }
  return i;
}
